const kurento = require("kurento-client");
var mqtt = require('mqtt');
var path = require('path');
var fs = require('fs');
var format = require('string-format');
const e = require("express");
format.extend(String.prototype);

class TKmsV10 {
    constructor() {
        this.config = {
            kmsUrl: "wss://wss.hhdata.cn:8433/kurento",
            tagKey: "tagKey-kms"
        }
        this.kmsClient = undefined;
        this.kmsObjByIds = {};
        this.kmsObjsByKey = {};
        this.registClientObjs = {};
    }
    async getClient() {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                if (me.kmsClient && me.kmsClient != null) {
                    success(me.kmsClient);
                } else {
                    kurento(me.config.kmsUrl, function(erClient, _client) {
                        me.kmsClient = _client;
                        _client.on('disconnect', function() {
                            delete me["kmsClient"];
                        })
                        _client.getServerManager().then(_svr => {
                            _client.svr = _svr;
                            _svr.on('ObjectDestroyed', (data => {
                                if (me.kmsObjs[data.objectId]) {
                                    var obj = me.kmsObjs[data.objectId];
                                    var key = obj.name;
                                    delete me.kmsObjs[data.objectId];
                                    delete me.kmsObjsByKey[key];
                                    //notify to mqtt
                                }
                            }));
                            var acPing = () => {
                                _client.getServerManager().then(_ => {
                                    console.log('ping server ...');
                                    setTimeout(acPing, 2 * 60 * 1000);
                                })
                            };
                            acPing();
                            _svr.getName().then(ds => {
                                success(me.kmsClient);
                            })
                        })
                    })
                }
            } catch (er) {
                console.log(er);
                success()
            }
        })
    }
    async scan() {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                var client = await me.getClient();
                var svr = client.svr;
                var ps = await svr.getPipelines();
                var getObjInfo = async function(obj) {
                    var objName = await obj.getName();
                    var info = {};
                    try {
                        var infoJson = await obj.getTag(me.config.tagKey);
                        info = JSON.parse(infoJson);
                    } catch (eer) {
                        info = {};
                    }
                    if (objName.indexOf(me.config.tagKey) == 0) {
                        objName = objName.replace(me.config.tagKey, '');
                        obj.name = objName;
                        obj.info = info;
                        me.kmsObjByIds[obj.id] = obj;
                        me.kmsObjsByKey[obj.name] = obj;
                        console.log(obj.name, obj.id);
                        var items = await obj.getChildren();
                        if (items.length > 0) {
                            items.forEach(async item => {
                                await getObjInfo(item);
                            })
                        } else {
                            success();
                        }
                    } else {
                        success();
                    }
                }
                ps.forEach(async p => {
                    await getObjInfo(p);
                })
            } catch (er) {
                console.log(er);
                success();
            }
        })
    }
    async setObjData(obj, objKey) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                var value = me.config.tagKey + objKey;
                await obj.setName(value);
                obj.name = objKey;
                me.kmsObjByIds[obj.id] = obj;
                me.kmsObjsByKey[objKey] = obj;
                success(obj);
            } catch (er) {
                console.log(er);
                success();
            }
        })
    }
    async setObjInfo(obj, info) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                var value = JSON.stringify(info);
                await obj.addTag(me.config.tagKey, value);
                obj.info = info;
                success(obj);
            } catch (er) {
                console.log(er);
                success();
            }
        })
    }
    async getSingleObj(objKey, objType, parentObjKey) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                if (me.kmsObjsByKey[objKey]) {
                    success(me.kmsObjsByKey[objKey]);
                } else {
                    var flag = objType == "MediaPipeline" || (me.kmsObjsByKey[parentObjKey] != null);
                    if (flag) {
                        var parentObj = me.kmsObjsByKey[parentObjKey];
                        if (objType == "MediaPipeline") {
                            var client = await me.getClient();
                            var pipe = await client.create(objType);
                            me.setObjData(pipe, objKey).then(obj => {
                                await obj.addTag(me.config.tagKey, JSON.stringify({}));
                                success(obj);
                            });
                        } else if (objType == "Dispatcher") {
                            var hub = await parentObj.create(objType);
                            me.setObjData(hub, objKey).then(obj => {
                                await obj.addTag(me.config.tagKey, JSON.stringify({}));
                                success(obj);
                            });
                        } else if (objType == "Alpha") {
                            var hub = await parentObj.create("AlphaBlending");
                            await me.setObjData(hub, objKey);
                            await hub.addTag(me.config.tagKey, JSON.stringify({}));

                            var masterHubPort = await hub.createHuibPort();
                            await me.setObjData(masterHubPort, objKey + "-masterHubPort");
                            await masterHubPort.addTag(me.config.tagKey, JSON.stringify({}));
                            await hub.setMaster(masterHubPort.id, 1);

                            var outputHubPort = await hub.createHuibPort();
                            await me.setObjData(outputHubPort, objKey + "-outputHubPort");
                            await outputHubPort.addTag(me.config.tagKey, JSON.stringify({}));

                            hub.masterHubPort = masterHubPort;
                            hub.outputHubPort = outputHubPort;

                            success(hub);
                        } else if (objType == "hubPort") {
                            var hub = me.getObjByKey(parentObjKey);
                            var hubPort = await hub.createHubPort();
                            await me.setObjData(hubPort, objKey);
                            await hubPort.addTag(me.config.tagKey, JSON.stringify({}));
                            success(hubPort);
                        }
                    } else {
                        success();
                    }
                }
            } catch (er) {
                console.log(er);
                success();
            }
        })
    }
    async connectHubPort(hubKey, srcKey, targetKey) {
        var me = this;
        var res = {
            hubStatus: 0,
            srcStatus: 0,
            targetStatus: 0
        }
        return new Promise(async(success, faild) => {
            try {
                var h = me.getObjByKey(hubKey);
                var s = me.getObjByKey(srcKey);
                var t = me.getObjByKey(targetKey);
                res.hubStatus = h && h != null ? 1 : 0;
                res.srcStatus = s && s != null ? 1 : 0;
                res.targetStatus = t && t != null ? 1 : 0;
                if (res.srcStatus > 0 && res.targetStatus > 0 && res.hubStatus > 0) {
                    h.connect(s, t, function(e) {
                        if (e) {
                            res.error = e;
                        }
                        success(res);
                    });
                } else {
                    success(res);
                }

            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async setAlphaChannel(alphaKey, channelKey, zOrder, left, top, width, height) {
        var me = this;
        var res = {
            alpha: 0,
            channel: 0
        }
        return new Promise(async(success, faild) => {
            try {
                var hub = me.getObjByKey(alphaKey);
                var channel = me.getObjByKey(channelKey);
                res.alpha = hub && hub != null ? 1 : 0;
                res.channel = channel && channel != null ? 1 : 0;
                if (res.channel > 0 && res.alpha > 0) {
                    hub.setPortProperties(1.0 * left, 1.0 * top, zOrder, 1.0 * width, 1.0 * height, channel.id, function(err) {
                        if (err) {
                            res.error = err;
                        }
                        success(res);
                    });
                } else {
                    success(res);
                }
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async releaseRegistClientObjs(clientId) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                me.getClient().then(_client => {
                    var objs = Object.values(me.registClientObjs).filter(o => {
                        return o.clientId == clientId;
                    });
                    objs.forEach(async o => {
                        var obj = await _client.getMediaobjectById(o.kmsId);
                        await obj.release();
                    })
                })
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async releaseObj(objKey) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                if (me.kmsObjsByKey[objKey]) {
                    me.kmsObjsByKey[objKey].release().then(_ => {
                        success();
                    })
                } else {
                    success();
                }
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async releaseObjs(objKeys) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                objKeys.forEach(async k => {
                    await me.releaseObj(k);
                })
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async applyTask(action, data) {
        var me = this;
        return new Promise(async(success, faild) => {
            try {
                if (action == "getSingleObj") {
                    var obj = await me.getSingleObj(data.objKey, data.ObjType, data.parentObjKey);
                    success(obj);
                } else if (action == "setObjInfo") {
                    if (me.kmsObjsByKey[data.objKey]) {
                        var obj = me.kmsObjsByKey[data.objKey];
                        var res = await me.setObjInfo(obj, data.info);
                        success(res);
                    } else {
                        success()
                    }
                } else if (action == "connectHubPort") {
                    var res = await me.getSingleObj(data.hubKey, data.srcKey, data.targetKey);
                    success(res);
                } else if (action == "setAlphaChannel") {
                    var res = await me.setAlphaChannel(data.alphaKey, data.channelKey, data.zOrder, data.left, data.top, data.width, data.height);
                    success(res);
                } else if (action == "releaseObj") {
                    var res = await me.releaseObj(data.objKey);
                    success(res);
                } else if (action == "releaseObjs") {
                    var res = await me.releaseObjs(data.objKeys);
                    success(res);
                }
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    async applyTasks(tasks) {
        var me = this;
        var res = [];
        return new Promise(async(success, faild) => {
            try {
                tasks.forEach(async task => {
                    var itemRes = await me.applyTask(task.action, task.data);
                    var item = JSON.parse(JSON.stringify(task));
                    item.result = (task.action == "getSingleObj" || task.action == "setObjInfo") ? { kmsId: itemRes.id, key: itemRes.name, info: itemRes.info } : itemRes;
                    res.push(item);
                })
                success(res);
            } catch (er) {
                console.log(er);
                success(res);
            }
        })
    }
    getStatus() {
        var me = this;
        var res = {
            kmsObjs: []
        };
        try {
            Object.values(me.kmsObjsByKey).forEach(o => {
                var data = {
                    kmsId: o.id,
                    key: o.name,
                    info: o.info
                }
                res.kmsObjs.push(data);
            })
            success(res);
        } catch (er) {
            console.log(er);
        }
        return res;
    }
    getPipeStatus(pipeKey) {
        var me = this;
        var res = {
            pipeKey: pipeKey,
            kmsObjs: []
        };
        try {
            var pipe = me.kmsObjsByKey[pipeKey];
            if (pipe && pipe != null) {
                Object.values(me.kmsObjsByKey).forEach(o => {
                    if (o.id.indexOf(pipe.id) >= 0) {
                        var data = {
                            kmsId: o.id,
                            key: o.name,
                            info: o.info
                        }
                        res.kmsObjs.push(data);
                    }
                })
                success(res);
            }
        } catch (er) {
            console.log(er);
        }
        return res;
    }
    getObjById(kmsId) {
        var me = this;
        var res = null;
        try {
            res = me.kmsObjByIds[kmsId];
        } catch (er) {
            console.log(er);
        }
        return res;
    }
    getObjByKey(objKey) {
        var me = this;
        var res = null;
        try {
            res = me.kmsObjsByKey[objKey];
        } catch (er) {
            console.log(er);
        }
        return res;
    }
    regist2WebSvr(webSvr) {
        var me = this;
        if (webSvr && webSvr != null && webSvr.registRoute) {
            var respJson = function(result, response) {
                try {
                    response.send(JSON.stringify(result));
                } catch (eer) {
                    console.log(eer);
                }
            }
            webSvr.registRoute("kms", "kmsv10", "getStatus", function(result, request, response) {
                try {
                    result.data = me.getStatus();
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "kmsv10", "getPipeStatus", function(result, request, response) {
                try {
                    result.data = me.getPipeStatus(result.ps.pipeKey);
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "kmsv10", "applyTask", function(result, request, response) {
                try {
                    var ac = result.ps.action;
                    var dataJson = result.ps.dataJson;
                    var data = JSON.parse(dataJson);
                    var res = await me.applyTask(ac, data);
                    result.data = (ac == "getSingleObj" || ac == "setObjInfo") ? { kmsId: res.id, key: res.name, info: res.info } : res;
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "kmsv10", "applyTasks", function(result, request, response) {
                try {
                    var tasksJson = result.ps.tasksJson;
                    var tasks = JSON.parse(tasksJson);
                    result.data = await me.applyTasks(tasks);
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "kmsv10", "registClientObj", function(result, request, response) {
                try {
                    me.registClientObjs[result.ps.kmsId] = {
                        kmsId: result.ps.kmsId,
                        clientId: result.ps.clientId
                    }
                    result.data = {
                        kmsId: result.ps.kmsId,
                        clientId: result.ps.clientId
                    };
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
            webSvr.registRoute("kms", "kmsv10", "releaseRegistClientObj", function(result, request, response) {
                try {
                    me.releaseRegistClientObjs(result.ps.clientId).then(_ => {
                        result.data = { status: 1 };
                        respJson(result, response);
                    })
                    respJson(result, response);
                } catch (err) {
                    console.log(err);
                    respJson(result, response);
                }
            });
        }
    }
}

module.exports = {
    TKmsV10: TKmsV10
};
/*
var k = new TKmsV10();
k.getClient().then(async _c => {
    await k.scan();
})
*/